package com.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName:TestApplicationContext
 * @Auther: xierongchang
 * @Description:
 * @Date: 2024/6/6 22:24
 * @Version: v1.0.0
 */
public class TestApplicationContext {

    private Class configClass;

    // bean定义map
    private Map<String, BeanDefinition> beanDefinitionMap =new HashMap<>();

    // 放单例bean
    private Map<String,Object> singletonObjects = new HashMap<>();

    private List<BeanPostProcess> beanPostProcessList = new ArrayList<>();

    public TestApplicationContext(Class configClass) {
        this.configClass = configClass;
        // 创建单例bean
        scan(configClass);

        // 找出单例bean
        for(String beanName:beanDefinitionMap.keySet()){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if("singleton".equals(beanDefinition.getScope())&&!beanDefinition.isLazy()){
                Object obj = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,obj);
            }
        }

    }



    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException("not found");
        }

        // 判断bean是否是单例
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if("singleton".equals(beanDefinition.getScope())){
             Object obj = singletonObjects.get(beanName);
            if (obj == null) {
                obj = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,obj);
            }
            return obj;
        }else {
            Object obj = createBean(beanName,beanDefinition);
            return obj;
        }
    }

    /**
     * 创建bean
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object createBean(String beanName,BeanDefinition beanDefinition){
        // 获取到bean的无参构造器创建bean
        Class clazz = beanDefinition.getClazz();
        Object instance = null;
        try {
            instance = clazz.getConstructor().newInstance();
            // 遍历到所有的属性
            for(Field field:clazz.getDeclaredFields()){
                if (field.isAnnotationPresent(Autowire.class)) {
                    field.setAccessible(true);
                    field.set(instance,getBean(field.getName()));
                }
            }
            // 执行初始化之前的代码
            for(BeanPostProcess beanPostProcess:beanPostProcessList){
                beanPostProcess.postProcessBeforeInitialization(clazz,beanName);
            }
            //初始化
            System.out.println(beanName);
            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).init();
            }
            // 执行初始化之后的代码
            for(BeanPostProcess beanPostProcess:beanPostProcessList){
                beanPostProcess.postProcessAfterInitialization(clazz,beanName);
            }

        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return instance;
    }

    private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value();
            // 需要将path转换为相对路径
            path = path.replace(".", "/");
            // 找到扫描路径下面所有的class
            ClassLoader classLoader = TestApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                for (File f : file.listFiles()) {
                    try {
                        BeanDefinition beanDefinition = new BeanDefinition();
                        String absolutePath= f.getAbsolutePath();
                        String classPath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                        classPath = classPath.replace("/",".");
                        Class<?> clazz = classLoader.loadClass(classPath);
                        beanDefinition.setClazz(clazz);
                        // 获取到所有需要扫描包下面的class，然后在判断是否有@Component注解
                        if (clazz.isAnnotationPresent(Component.class)) {
                            // 判断当前类是否实现了BeanPostProcess接口
                            if (BeanPostProcess.class.isAssignableFrom(clazz)) {
                                BeanPostProcess process = (BeanPostProcess) clazz.getConstructor().newInstance();
                                beanPostProcessList.add(process);
                            }

                            Component component= clazz.getAnnotation(Component.class);
                            String beanName  = component.value();
                            if("".equals(beanName)){
                                beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }
                            // 如果包含注解，需要判断是否bean为单例
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                // 如果包含scope，判断value是否为singleton
                                Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            }else {
                                // 单例bean
                                beanDefinition.setScope("singleton");
                            }
                            if (clazz.isAnnotationPresent(Lazy.class)) {
                                beanDefinition.setLazy(true);
                            }
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}
